class UseDecorator(object):
    ERR_MSG = 'Could not use repository \'%s\', was not found in object %s'
    def __init__(self, repo_name, *sub_stores):
        self.repo_name = repo_name
        self.sub_stores = sub_stores

    def __call__(self, f): 
        def wrapper(self_obj, *args, **kwargs):
            if not hasattr(self_obj, self.repo_name):
                raise AttributeError(self.ERR_MSG % (self.repo_name, self_obj))

            with getattr(self_obj, self.repo_name).use(*self.sub_stores) as s:
                if isinstance(s, list):
                    return f(self_obj, *args + tuple(s), **kwargs)
                else:
                    return f(self_obj, *args + (s,), **kwargs)
        return wrapper

use = UseDecorator

class Repository(object):
    ERR_MSG = 'Could not find item named \'%s\' in store.'
    def __init__(self, store):
        self.store = store
        self.repositories = {}
        self.built_repositories = {}

    def add_repository(self, name, builder):
        self.repositories[name] = builder

    def use(self, *store_names):
        data = self.store.use()
        requested = []

        for store_name in store_names:
            if store_name not in data:
                data[store_name] = {}

            store_data = data[store_name]
            if store_name in self.repositories:
                if store_name not in self.built_repositories:
                    builder = self.repositories[store_name]
                    self.built_repositories[store_name] = builder(store_data)

                requested.append(self.built_repositories[store_name])
            else:
                requested.append(store_data)

        if len(requested) == 1:
            return RepositoryContextManager(self.store, requested[0])
        else:
            return RepositoryContextManager(self.store, requested)

class RepositoryContextManager(object):
    def __init__(self, store, requested_store):
        self.store = store
        self.requested_store = requested_store

    def __enter__(self):
        return self.requested_store
    
    def __exit__(self, exc_type, exc_value, tb):
        self.store.save()
